package nf.graph;

import java.util.Locale;
import java.lang.reflect.*;

public interface PropertyBag
{
	public Object getProperty(String name);
	public Object setProperty(String name, Object newValue);
	
	public static class Access
	{
		private Access()
		{
		
		}
		
		private static String capitalize(String s)
		{
			return s.substring(0, 1).toUpperCase(Locale.ENGLISH) +
				s.substring(1);
		}
	
		public static Object get(PropertyBag bag, String propertyName)
		{
			Class<?> bagClass = bag.getClass();
			String methodName = "get" + capitalize(propertyName);
			Method method;
			
			try {
				method = bagClass.getMethod(methodName);
			} catch (NoSuchMethodException e) {
				method = null;
			}
			
			if (method != null) {
				try {
					return method.invoke(bag);
				} catch (IllegalAccessException e) {
					throw new RuntimeException("Failed to call the property" +
						" getter for '" + propertyName + "'.", e);
				} catch (InvocationTargetException e) {
					throw new RuntimeException("Failed to call the property" +
						" getter for '" + propertyName + "'.", e);
				}
			}
			
			return bag.getProperty(propertyName);
		}
		
		public static Object set(PropertyBag bag, String name, Object value)
		{
			Class<?> bagClass = bag.getClass();
			
			String methodName = "set" + capitalize(name);
			Method method = null;
			Class<?> valueClass = value.getClass();
			
			do {
				try {
					method = bagClass.getMethod(methodName, valueClass);
					break;
				} catch (NoSuchMethodException e) {
					valueClass = valueClass.getSuperclass();
				}
			} while (valueClass != null);
			
			if (method != null) {
				try {
					return method.invoke(bag, value);
				} catch (IllegalAccessException e) {
					throw new RuntimeException("Failed to call the property" +
						" setter for '" + name + "'.", e);
				} catch (InvocationTargetException e) {
					throw new RuntimeException("Failed to call the property" +
						" setter for '" + name + "'.", e);
				}
			}
			
			return bag.setProperty(name, value);
		}
	}
}
